package com.grad.webstudy.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.grad.webstudy.constants.RedisConstants;
import com.grad.webstudy.constants.RoleConstants;
import com.grad.webstudy.constants.UserContants;
import com.grad.webstudy.context.UserContext;
import com.grad.webstudy.dao.UserDao;
import com.grad.webstudy.dto.ResetPasswordDto;
import com.grad.webstudy.enums.ExceptionEnums;
import com.grad.webstudy.enums.UserStatusEnum;
import com.grad.webstudy.exceptions.CodeException;
import com.grad.webstudy.exceptions.CustomException;
import com.grad.webstudy.exceptions.UserException;
import com.grad.webstudy.model.RoleUser;
import com.grad.webstudy.model.User;
import com.grad.webstudy.service.*;
import com.grad.webstudy.utils.RedisUtil;
import com.grad.webstudy.vo.UserVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * @Description:
 * @Author: zjf
 * @Date: 2020/1/14 11:21.
 */
@Service
@Transactional
public class UserServiceImpl implements UserService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());


    @Autowired
    private RedisUtil redisUtil;


    @Autowired
    private UserDao userDao;


    @Autowired
    private RoleService roleService;


    @Autowired
    private AuthorityService authorityService;

    @Autowired
    private RoleAuthService roleAuthService;


    @Autowired
    private RoleUserService roleUserService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public int register(String phone, String password, String validCode, String systemCode) {
        User user = new User();
        if (!checkPhone(phone)) {
            throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(),"手机号已注册");
        }
        if (!Objects.equals(validCode, systemCode)) {
            throw new CodeException(ExceptionEnums.VALID_CODE_ERROR.getMessage());
        }
        user.setEnableComment(UserStatusEnum.ENABLE_COMMENT.status);
        user.setEnableLogin(UserStatusEnum.ENABLE_LOGIN.status);
        user.setPhone(phone.trim());
        user.setPassword(password);
        user.setImagePath("http://localhost:8081/files/20200529155150GgfQHQYA.png");
//        ResourceUtils.getFile("classpath:image/header/default.jpg");
        userDao.register(user);

        RoleUser roleUser = new RoleUser();
        roleUser.setRoleId(RoleConstants.EVERYONE);
        roleUser.setUserId(user.getId());
        //判断默认用户角色设置是否成功 成功返回用户id，失败返回-1
        redisTemplate.delete(phone);
        return roleUserService.addUserRole(roleUser) ? user.getId() : -1;
    }


    public int addUser(String username, String password, String phone) {
        User user = new User();
        user.setEnableComment(UserStatusEnum.ENABLE_COMMENT.status);
        user.setEnableLogin(UserStatusEnum.ENABLE_LOGIN.status);
        if (!checkPhone(phone)) {
            throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(),"手机号已存在");
        }
        user.setPhone(phone.trim());
        user.setPassword(password);
        user.setUsername(username);

        user.setImagePath("http://localhost:8081/files/20200528201004KRcs3kJo.jpg");


        userDao.register(user);

        RoleUser roleUser = new RoleUser();
        roleUser.setRoleId(RoleConstants.EVERYONE);
        roleUser.setUserId(user.getId());

        redisUtil.delByPrefix(RedisConstants.REDIS_USER_KEY_PREFIX);
        //判断默认用户角色设置是否成功 成功返回用户id，失败返回-1
        return roleUserService.addUserRole(roleUser) ? user.getId() : -1;
    }

    @Override
    public boolean checkPhone(String phone) {
        return userDao.checkPhone(phone.trim()).size() > 0 ? false : true;
    }

    @Override
    public User login(String phone, String password, String validCode, String systemCode) {
        User user = null;
        //输入的密码不为空代表是密码登录方式
        if (password != null && password.trim().length() > 0) {
            user = userDao.login(phone.trim(), password.trim());
            if (Objects.isNull(user)) {
                throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(),"手机号或密码错误");
            }
        } else {
            if (!Objects.equals(validCode, systemCode)) {
                throw new CodeException(ExceptionEnums.VALID_CODE_ERROR.getMessage());
            }
            user = userDao.login(phone.trim(), null);

            if (Objects.isNull(user)) {
                throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(),"手机号未注册");
            }
        }
        if (user.getEnableLogin() == UserStatusEnum.UNABLE_LOGIN.status) {
            throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(),"禁止登录");
        }
        if (Objects.isNull(user.getUsername())){
            user.setUsername(user.getPhone());
        }
        user.setPassword("");
        return user;

    }

    @Override
    public User loginForAdmin(String username, String password) {
        User user = null;

        if (password != null && password.trim().length() > 0) {
            user = userDao.loginForAdmin(username, password);
            if (Objects.isNull(user)) {
                throw new UserException("账号或密码错误");
            }
        }

        return user;
    }

    @Override
    public PageInfo queryAllUsers(int currPage, int pageSize, String keyword) {


        PageInfo<UserVo> userPageInfo;

        Page page = PageHelper.startPage(currPage, pageSize);
        List<User> users = userDao.queryAllUsers(keyword.trim());

        List<UserVo> result = new ArrayList<>(users.size());

        for (User user : users) {

            UserVo userVo = convert(user);

            result.add(userVo);
        }
        userPageInfo = new PageInfo<>(result);
        userPageInfo.setTotal(page.getTotal());
        userPageInfo.setPageNum(currPage);
        userPageInfo.setPageSize(pageSize);


        return userPageInfo;
    }

    @Override
    public boolean changeUserStatus(int userId, int status) {
        if (userId == UserContants.DEV_ID) {
            throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(), "不能更改系统开发者状态");
        }
        if (Objects.isNull(userDao.selectById(userId))) {
            throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(), "用户Id=[" + userId + "]不存在");
        }
        String field = "";
        if (status == UserStatusEnum.ENABLE_LOGIN.status || status == UserStatusEnum.UNABLE_LOGIN.status) {
            field = UserContants.Fields.ENABLE_LOGIN;
        } else if (status == UserStatusEnum.ENABLE_COMMENT.status || status == UserStatusEnum.UNABLE_COMMENT.status) {
            field = UserContants.Fields.ENABLE_COMMENT;
        }
        redisUtil.delByPrefix(RedisConstants.REDIS_USER_KEY_PREFIX);
        return userDao.changeUserStatus(userId, field, status) > 0 ? true : false;
    }

    @Override
    public boolean deleteUsersByIds(String userIds) {


        String[] ids = userIds.split(",");
        List<String> userIdslist = new ArrayList<String>(ids.length);
        Collections.addAll(userIdslist, ids);

        if (ObjectUtils.containsElement(userIdslist.toArray(), UserContants.DEV_ID)) {
            throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(), "不能删除系统开发者账号");
        }

        redisUtil.delByPrefix(RedisConstants.REDIS_USER_KEY_PREFIX);
        return userDao.deleteUsersByIds(userIdslist) == ids.length ? true : false;
    }

    @Override
    public boolean resetPassword(ResetPasswordDto resetPasswordDto, String systemCode) {

        User loginUser = UserContext.getLoginUser();
        List<Integer> roleIds = roleUserService.queryRoleIdsByUserId(loginUser.getId());

        //如果用户角色包含管理员角色，
        if (roleIds.contains(RoleConstants.ADMIN)) {
            // todo 管理员以普通用户方式重置密码
            return resetPasswordForAdmin(resetPasswordDto);
        }
        return resetPasswordForEveryOne(resetPasswordDto, systemCode);

    }


    /**
     * 普通用户重置自己的密码 todo
     *
     * @param resetPasswordDto
     * @param systemCode
     * @return
     */
    private boolean resetPasswordForEveryOne(ResetPasswordDto resetPasswordDto, String systemCode) {
        String validCode = resetPasswordDto.getValidCode();

        //手机号-验证码方式重置
        if (validCode != null && validCode.trim().length() > 0) {
            if (!Objects.equals(validCode, systemCode)) {
                throw new CodeException(ExceptionEnums.VALID_CODE_ERROR.getMessage());
            }
            return userDao.resetPassword(resetPasswordDto.getPhone(), null, null, resetPasswordDto.getNewPassword()) > 0;
        }

        // 手机号-旧密码方式重置
        String oldPassword = resetPasswordDto.getOldPassword();
        String phone = resetPasswordDto.getPhone();
        if (oldPassword != null && oldPassword.trim().length() > 0
                && phone != null && phone.trim().length() > 0) {
            return userDao.resetPassword(phone, null, oldPassword, resetPasswordDto.getNewPassword()) > 0;
        }

        // 直接旧密码重置
        User loginUser = UserContext.getLoginUser();
        if (oldPassword != null && oldPassword.trim().length() > 0) {
            return userDao.resetPassword(null, loginUser.getId(), oldPassword, resetPasswordDto.getNewPassword()) > 0;
        }
        return false;
    }

    /**
     * 管理员重置密码
     *
     * @param resetPasswordDto
     * @return
     */
    private boolean resetPasswordForAdmin(ResetPasswordDto resetPasswordDto) {
        User adminUser = UserContext.getLoginUser();
        //管理员重置他人密码
        if (adminUser.getId() != resetPasswordDto.getUserId()) {
            return userDao.resetPassword(null, resetPasswordDto.getUserId(), null, resetPasswordDto.getNewPassword()) > 0;
        }
        //重置管理员自己密码
        return userDao.resetPassword(null, adminUser.getId(), null, resetPasswordDto.getNewPassword()) > 0;

    }

    @Override
    public boolean updateUserRole(int userId, String roleIds) {

        if (userId == UserContants.DEV_ID) {
            throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(), "不能更改系统开发者角色");
        }

        String[] roles = roleIds.split(",");
        Set<Integer> cIds = new HashSet<>();

        for (String role : roles) {
            cIds.add(Integer.valueOf(role));
        }
        //判断数据库表中，用户角色id哪些存在，哪些不存在

        //根据用户id查询用户原本的角色id
        List<Integer> userRoleIds = roleUserService.queryRoleIdsByUserId(userId);

        List<Integer> addRoleIds = new ArrayList<>();//需要新添加的角色id


        for (Integer roleId : cIds) {
            if (userRoleIds.contains(roleId)) {
                // 如果用户原本的角色id包含新修改的，则从用户原本角色id移除，
                // 这样的userRoleIds最后只剩下需要删除的角色id
                userRoleIds.remove(roleId);
            } else {
                //如果新增的角色id，用户原来没有，则需要在数据库新增
                addRoleIds.add(roleId);
            }
        }


        //查看新增的角色id中是否合法
        if (addRoleIds.size() > 0) {
            if (!roleService.checkRoleIds(addRoleIds)) {
                throw new CustomException(ExceptionEnums.CUSTOM_ERROR.getCode(),
                        "传入的角色id有误");
            }
        }

        //需要新增的映射关系
        List<RoleUser> addRoleUser = combineByRoleIds(userId, addRoleIds);

        //需要删除的映射关系
        List<RoleUser> delRoleUser = combineByRoleIds(userId, userRoleIds);

        boolean add = true;
        boolean del = true;

        if (addRoleUser.size() > 0) {
            add = roleUserService.addUserRoles(addRoleUser);
        }
        if (delRoleUser.size() > 0) {
            del = roleUserService.deleteRoleUsers(delRoleUser);
        }
        redisUtil.delByPrefix(RedisConstants.REDIS_USER_KEY_PREFIX);

        return add && del;

    }

    @Override
    public boolean updateHeadImage(int userId, String imagePath) {

        return userDao.updateHeadImage(userId, imagePath) > 0;
    }

    @Override
    public UserVo queryUserById(int userId) {

        User user = userDao.selectById(userId);
        user.setPassword("");
        UserVo userVo = convert(user);
        List<Integer> roleIds = roleUserService.queryRoleIdsByUserId(user.getId());
        userVo.setRoles(roleService.getRoleNameByIds(roleIds));

        return userVo;
    }

    @Override
    public boolean updateUserById(User user) {
        redisUtil.delByPrefix(RedisConstants.REDIS_USER_KEY_PREFIX);
        return userDao.updateUserById(user) > 0 ? true : false;
    }

    @Override
    public User selectUserForSystem(int userId) {
        return userDao.selectById(userId);
    }

    @Override
    public List<String> queryUserAuth(int userId) {
        List<String> authList = roleAuthService.queryAuthIdsByUserId(userId);
        Set<String> authIds = new HashSet<>();
        for (String s : authList) {
            String[] ids = s.split(",");
            for (String id : ids) {
                authIds.add(id);
            }
        }
        return authorityService.queryAuthDeatilByIds(authIds);
    }

    @Override
    public Integer queryUserCount() {
        return userDao.queryUserCount();
    }

//    public List<User>

    /**
     * 根据一个用户id，角色ids组合映射关系
     *
     * @param userId
     * @param roleIds
     * @return
     */
    private List<RoleUser> combineByRoleIds(int userId, List<Integer> roleIds) {
        List<RoleUser> result = new ArrayList<>();
        for (Integer roleId : roleIds) {
            RoleUser roleUser = new RoleUser();
            roleUser.setUserId(userId);
            roleUser.setRoleId(roleId);
            result.add(roleUser);
        }
        return result;
    }


    /**
     * 实体类转换为Vo
     *
     * @param user
     * @return
     */
    private UserVo convert(User user) {
        return new UserVo.Builder()
                .setDuration(user.getDuration())
                .setEnableComment(user.getEnableComment())
                .setEnableLogin(user.getEnableLogin())
                .setId(user.getId())
                .setImagePath(user.getImagePath())
                .setPhone(user.getPhone())
                .setRegisterTime(user.getRegisterTime())
                .setUsername(user.getUsername())
                .setRoles(roleUserService.queryUserRoleName(user.getId()))
                .build();
    }


}
